﻿using System;
using System.Collections.Generic;

namespace AlgorytmyGenetyczne.Operatory
{
    public class SelekcjaRuletki : IOperator
    {
        private readonly List<DystrybuantaOsobnika> listaDystrybuantOsobnikow;
        private readonly Random losowanie;
        public Populacja WynikSelekcji { get; private set; }
        public bool Elitarnosc { get; set; }
        public int WielkoscElity { get; set; }

        public SelekcjaRuletki()
        {
            losowanie = new Random();
            listaDystrybuantOsobnikow = new List<DystrybuantaOsobnika>();
            WynikSelekcji = new Populacja();
            Elitarnosc = false;
            WielkoscElity = 1;
        }

        public Populacja wykonaj(Populacja populacja)
        {
            
            WynikSelekcji.Osobnicy.Clear();
            policzDystrybuanty(populacja);
            List<Osobnik> osobnicy = populacja.Osobnicy;

            for (int i = 0; i < osobnicy.Count; i++)
            {
                double los = losowanie.NextDouble();
                foreach (DystrybuantaOsobnika dos in listaDystrybuantOsobnikow)
                {
                    if (los <= dos.Dystrybuanta)
                    {
                        WynikSelekcji.Osobnicy.Add(new Osobnik(dos.Osobnik));
                        break;
                    } 
                }
            }
            if(Elitarnosc)
            {
                osobnicy.Sort((osobnik1, osobnik2) => osobnik2.Ocena.CompareTo(osobnik1.Ocena));
                for (int i = 0; i < WielkoscElity; i++)
                {
                    Osobnik osobnikElitarny = osobnicy[i];
                    if (!WynikSelekcji.Osobnicy.Contains(osobnikElitarny))
                        WynikSelekcji.Osobnicy.Add(new Osobnik(osobnikElitarny));
                }
                WynikSelekcji.Osobnicy.Sort((osobnik1, osobnik2) => osobnik2.Ocena.CompareTo(osobnik1.Ocena));

                int liczbaOsobnikowDoUsuniecia = WynikSelekcji.Osobnicy.Count - populacja.Osobnicy.Count;
                for (int i = 0; i < liczbaOsobnikowDoUsuniecia; i++)
                {
                    WynikSelekcji.Osobnicy.RemoveAt(WynikSelekcji.Osobnicy.Count-1);
                }
            }
            return new Populacja(WynikSelekcji);
        }

        private void policzDystrybuanty(Populacja populacja)
        {
            listaDystrybuantOsobnikow.Clear();
            List<Osobnik> osobnicy = populacja.Osobnicy;

            double sumaOcen = 0;

            foreach (Osobnik osobnik in osobnicy)
            {
                sumaOcen += osobnik.Ocena;
            }

            double wartoscDystrybuanty = 0;
            foreach (Osobnik osobnik in osobnicy)
            {
                wartoscDystrybuanty += osobnik.Ocena/sumaOcen;
                listaDystrybuantOsobnikow.Add(new DystrybuantaOsobnika(wartoscDystrybuanty, osobnik));
            }
            listaDystrybuantOsobnikow.Sort((do1, do2) => do1.Dystrybuanta.CompareTo(do2.Dystrybuanta));
        }
    }
}
